#include "codegen.h"

int n_labels = 0;

char * gen_func(char *entete, char *corps)
{
  char buf[10];
  sprintf(buf, "%d", 200);
  char *func = concat(entete, 
		      "\tsubl\t$", buf, ", %esp\n",
		      corps, 
		      "\tpopl\t%ebp\n",
		      "\tret\n",
		      NULL);
  return func;
}

char * gen_entete(returntype rettype, char *nom, struct arguments *args)
{
  add_entete(rettype, nom, args);
  char buf[10];
  sprintf(buf, "%d", rettype);
  char *entete = concat("\n\t.globl ", nom,
			"\n\t.type ", nom, "@function\n", 
			nom, ":\n",
			"\tpushl\t%ebp\t\t # sauvegarde ancien ebp\n",
			"\tmovl\t%esp, %ebp\t\t# met a jour ebp\n\n",
			NULL);
  debut_local();
  return entete;
}

char * gen_call(char *nom, struct arguments *args)
{
	symbole * s = lookup_exec(nom);
	if (s != NULL && s->mtype == FONCTION){
		int nb = s->func_nb_args;
		if (argum == nb)
			return concat("\tcall\t", nom, "\n", NULL);
		else
			yyerror{"Nombre d'arguments incorrect\n"};
	}
	return concat("", NULL);
}

char * gen_expr(int op, char *expr1, char *expr2)
{
  char *str;
  char buf[10], buf2[10], buf3[10]; // Pour les labels dans ET et OU
    
  switch(op)
    {
    case PLUS:
      return concat("# Addition\n",
		    expr1, expr2, 
		    "\tpopl\t%eax\n",
		    "\tpopl\t%edx\n",
		    "\taddl\t%edx, %eax\n",
		    "\tpushl\t%eax\n",
		    NULL);
      break;
    case MOINS:
      return concat("# Soustraction\n",
		    expr1, expr2, 
		    "\tpopl\t%eax\n",
		    "\tpopl\t%edx\n",
		    "\tsubl\t%eax, %edx\n",
		    "\tpushl\t%edx\n",
		    NULL);
      break;
    case FOIS:
      return concat("# Multiplication\n",
		    expr1, expr2, 
		    "\tpopl\t%eax\n",
		    "\tpopl\t%edx\n",
		    "\imull\t%edx, %eax\n",
		    "\tpushl\t%eax\n",
		    NULL);
      break;
    case DIVISE:
      return concat("# Division\n",
		    expr1, expr2, 
		    "\tpopl\t%eax\n",
		    "\tpopl\t%ecx\n",
		    "\tidivl\t%ecx\n",
		    "\tpushl\t%eax\n",
		    NULL);
      break;
    case MODULO:
      return concat("# Modulo\n",
		    expr1, expr2, 
		    "\tpopl\t%eax\n",
		    "\tpopl\t%ecx\n",
		    "\tidivl\t%ecx\n",
		    "\tpushl\t%edx\n",
		    NULL);
      break;    
    case INF:
      return concat("# Inférieur\n",
		    expr1, expr2, 
		    "\tpopl\t%eax\n",
		    "\tpopl\t%edx\n",
		    "\tcmpl\t%eax, %edx\n",
		    "\tsetl\t%eax\n",
		    "\tpushl\t%eax\n",
		    NULL);
      break;    
    case INF_EG:
      return concat("# Inférieur ou égal\n",
		    expr1, expr2, 
		    "\tpopl\t%eax\n",
		    "\tpopl\t%edx\n",
		    "\tcmpl\t%eax, %edx\n",
		    "\tsetle\t%eax\n",
		    "\tpushl\t%eax\n",
		    NULL);
      break;    
    case SUP:
      return concat("# Supérieur\n",
		    expr1, expr2, 
		    "\tpopl\t%eax\n",
		    "\tpopl\t%edx\n",
		    "\tcmpl\t%eax, %edx\n",
		    "\tsetg\t%eax\n",
		    "\tpushl\t%eax\n",
		    NULL);
      break;    
    case SUP_EG:
      return concat("# Supérieur ou égal\n",
		    expr1, expr2, 
		    "\tpopl\t%eax\n",
		    "\tpopl\t%edx\n",
		    "\tcmpl\t%eax, %edx\n",
		    "\tsetge\t%eax\n",
		    "\tpushl\t%eax\n",
		    NULL);
      break;    
    case DIF:
      return concat("# Différent de\n",
		    expr1, expr2, 
		    "\tpopl\t%eax\n",
		    "\tpopl\t%edx\n",
		    "\tcmpl\t%eax, %edx\n",
		    "\tsetne\t%eax\n",
		    "\tpushl\t%eax\n",
		    NULL);
      break;    
    case EGAL:
      return concat("# Égal\n",
		    expr1, expr2, 
		    "\tpopl\t%eax\n",
		    "\tpopl\t%edx\n",
		    "\tcmpl\t%eax, %edx\n",
		    "\tsete\t%eax\n",
		    "\tpushl\t%eax\n",
		    NULL);
      break;
    case ET:
      sprintf(buf, "%d", n_labels++);
      sprintf(buf2, "%d", n_labels++);
      return concat("# ET\n",
		    expr1, expr2,		    
		    "\tpopl\t%eax\n",
		    "\tpopl\t%edx\n",
		    "\tcmpl\t$0, %eax",
		    "\tje\t.label", buf, "\n",
		    "\tcmpl\t$0, %eax",
		    "\tje\t.label", buf, "\n",
		    "\tmovl\t$1, %eax\n"
		    "\tjmp\t.label", buf2, "\n",
		    ".label", buf, ":\n",
		    "\tmovl\t$0, %eax\n",
		    ".label", buf2, ":\n",		    
		    "\tpushl\t%eax\n",
		    NULL);
      break;
    case OU:
      sprintf(buf, "%d", n_labels++);
      sprintf(buf2, "%d", n_labels++);
      sprintf(buf3, "%d", n_labels++);
      return concat("# OU\n",
		    expr1, expr2,		    
		    "\tpopl\t%eax\n",
		    "\tpopl\t%edx\n",
		    "\tcmpl\t$0, %eax",
		    "\tjne\t.label", buf, "\n",
		    "\tcmpl\t$0, %eax",
		    "\tje\t.label", buf2, "\n",
		    ".label", buf, ":\n",
		    "\tmovl\t$1, %eax\n",
		    "\tjmp\t.label", buf3, "\n",
		    ".label", buf2, ":\n",
		    "\tmovl\t$0, %eax\n",
		    ".label", buf3, ":\n",
		    "\tpushl\t%eax\n",
		    NULL);
      break;
    case MOINS_UNAIRE:
      return concat("# MOINS_UNAIRE\n",
		    expr1,
		    "\tpopl\t%eax\n",
		    "\tnegl\t%eax\n",
		    "\tpushl\t%eax\n",
		    NULL);
      break;
    case NOT:
      return concat("# NOT\n",
		    expr1,
		    "\tpopl\t%eax\n",
		    "\tcmpl$0, %eax\n",
		    "\tsete\t%eax\n",
		    "\tpushl\t%eax\n",
		    NULL);
      break;
    }
  return str;
}

char * gen_term(char *nom)
{
  symbole *s;
  if((s = lookup_exec(nom)) == NULL)
    {
      return "";
    }
  else
    {
      char buf[10];
      sprintf(buf, "%s", s->var_adresse);
      return concat("\tpushl\t", buf, "\n", NULL);
    }
}

char * gen_assign(char *nom, char *expr)
{
  symbole *s = lookup_exec(nom);
  if(s != NULL)
    {
      if(s->mtype == VARIABLE)
	{
	  return concat(expr,			
			"\tpopl\t", s->var_adresse, "\n", 
			NULL);
	}
      else
	{
	  char *message = concat("L'identifiant \"", nom, "\" n'est pas une variable.\n", NULL);
	  yyerror(message);
	  free(message);
	}
    }
  else
    {
      return concat("Erreur\n", NULL);
    }
}

char * gen_if(char *expr, char *instr)
{
  char label[10];
  sprintf(label, "%d", n_labels++);
  char *str = concat(expr,
		     "\tcmpl\t$0, %eax\n\tjz\t.label",
		     label,
		     "\n",
		     // instructions
		     instr,
		     // label 1
		     ".label",
		     label,
		     ":\n",
		     NULL);
  return str;
}

char * gen_if_else(char *expr, char *instr1, char *instr2)
{
  char label1[10], label2[10];
  sprintf(label1, "%d", n_labels++);
  sprintf(label2, "%d", n_labels++);
  char *str = concat(expr,
		     "\tcmpl\t$0, %eax\n\tjz\t.label", label1, "\n",
		     // instructions if
		     instr1,
		     "jz\t.label", label2, "\n",
		     // label 1
		     ".label", label1, ":\n",
		     //instructions else
		     instr2,
		     // label 2
		     ".label", label2, ":\n",
		     NULL);
  return str;
}

char * gen_while(char *expr, char *instr)
{
  char label1[10], label2[10];
  sprintf(label1, "%d", n_labels++);
  sprintf(label2, "%d", n_labels++);
  char *str = concat( // label 1
		     ".label", label1, ":\n",
		     expr,
		     "\tcmpl\t$1,\t%eax\n\tjz\t.label", label2, "\n",
		     //instructions
		     instr,
		     "\tjz\t.label1", label1, "\n",
		     // label 2
		     ".label", label2,
		     ":\n",
		     NULL);
  return str;
}

char * gen_exit(int entier)
{ 
  char *str = concat( "\tmov eax,\t$1\n\tmov ebx,\t$",
		      entier,
		      "\n\tint $0x80 \n", NULL);
}

char * gen_return()
{
  char * str = concat( "\tret\n", NULL);
  return str;
}

char * gen_return_exp(char * expr)
{
  char * str = concat(expr, 
		      "\tpopl\t%eax\n",   
		      "\tret\n", 
		      NULL);
  return str;
}

void dump(char* code)
{
  printf("Ecriture dans le fichier assembleur...\n");
  FILE *fic = fopen("assembleur.s", "w");
  fputs(code, fic);
}
